/// <reference path='lightLine.d.ts' />
import { 
  AdditiveBlending,
  CatmullRomCurve3, 
  Color, 
  DoubleSide, 
  Mesh, 
  Scene, 
  ShaderMaterial, 
  TubeBufferGeometry, 
  Vector3, 
  WebGLRenderer
} from "three";

// 片元着色器
const fragmentShader = `
#include <common>

uniform vec2 iResolution;
uniform float iTime;
// 接收颜色
uniform vec3 iColor;
// 背景色
uniform vec3 iBgColor;

float box(in vec2 _st, in vec2 _size){
  _size = vec2(0.5) - _size*0.5;
  vec2 uv = smoothstep(_size, _size+vec2(0.001), _st);
  uv *= smoothstep(_size, _size+vec2(0.001), vec2(1.0)-_st);
  return uv.x*uv.y;
}

float cross(in vec2 _st, float _size){
  return  box(_st, vec2(_size/4.0, _size));
}

varying vec2 vUv;

void main(){
  vec2 fragCoord = vUv * iResolution.xy;
  vec2 st = fragCoord.xy / iResolution.xy;
  vec3 color = iBgColor;

  // st -= vec2(0.5);

  // To move the cross we move the space
  // vec2 translate = vec2(0, (iTime - 50.) / 50.);
  vec2 translate = vec2(-(iTime - 50.) / 50., 0.);
  // vec2 translate = vec2(-0.5, 0.);
  st += translate * 0.7;

  // Add the shape on the foreground
  color += vec3(cross(st, 1.0)) * iColor;

  gl_FragColor = vec4(color, 1.0);
}
`;

const vertexShader = `
#include <common>

uniform float step;
varying vec2 vUv;

void main() {
  vUv = uv;

  gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`;

class LightLine {
  lines: any[] = [];
  /**
   * 创建流动光线
   * @param lines 线的路径数组
   * @param renderer WebGLRenderer
   * @param scene Scene 场景
   * 
   * @example
   * const lightLine = new LightLine(  
   *  {  
   *    points: [0, 0, 0, 10, 0, 10,],
   *    size: 0.1,  
   *    duration: 3000,  
   *    delay: 0,  
   *    color: 0xffffff,  
   *    bgColor: 0x565656  
   *  },  
   *  renderer,  
   *  scene   
   * )
   * 
   */
  constructor(lines: INT_Line[], renderer: WebGLRenderer, scene: Scene) {
    lines.forEach(line => {
      this.lines.push( new DrawLine(line, renderer, scene) )
    })
  }

  update() {
    this.lines.forEach(line => {
      line.update()
    })
  }
}

class DrawLine {
  private startTime: number = Date.now();
  points: INT_Points[];
  size: number;
  duration: number;
  delay: number;
  color: number;
  bgColor: number;
  material: ShaderMaterial;
  scene: Scene;

  constructor(line: INT_Line, renderer: WebGLRenderer, scene: Scene) {
    this.points = this.toFormatPoints(line.points);
    this.size = line?.size ?? 0.05;
    this.duration = line?.duration ?? 1000;
    this.delay = line?.delay ?? 0;
    this.color = line?.color ?? 0xffffff;
    this.bgColor = line?.bgColor ?? 0x000000;
    this.scene = scene;

    const canvas = renderer.domElement;

    this.material = new ShaderMaterial({
      transparent: true,
      fragmentShader,
      vertexShader,
      side: DoubleSide,
      // blending: AdditiveBlending,
      uniforms: {
        iTime: { value: 0 },
        iColor: { value: new Color(this.color) },
        iBgColor: { value: new Color(this.bgColor) },
        iResolution: { value: new Vector3(canvas.width, canvas.height, 1) }
      }
    });

    this.drawLine();
  }

  toFormatPoints(args: number[]): INT_Points[] {
    let arr = []
    for (let i = 0; i < args.length; i +=3) {
      let [x = 0, y = 0, z = 0] = args.slice(i, i +3);
      arr.push({x, y, z})
    }

    return arr
  }

  drawLine() {
    let points = this.points.map(item => {
      return new Vector3(item.x, item.y, item.z)
    });
    let lineCur = new CatmullRomCurve3(points);
    let geom = new TubeBufferGeometry(lineCur, 100, this.size, 8, false);
    let mesh = new Mesh(geom, this.material);
    
    this.scene.add(mesh);
  }

  update() {
    const nowTime = Date.now();
    const useTime = nowTime - this.startTime;
    let progress = 0;

    if (useTime > this.delay) {
      progress = (useTime - this.delay) % this.duration / this.duration;
    }

    this.material.uniforms.iTime.value = progress * 100;
  }
}

export {
  LightLine
}